#ifndef PW_PBL_RANGEDINT_HPP
#define PW_PBL_RANGEDINT_HPP

#include <bit>
#include <type_traits>
#include "RustMimic.hpp"
#include "IntFor.hpp"
#include "Math.hpp"

namespace pw_pbl::_ranged_int
{
	template <size_t BITS>
	concept ccAlignedBits = std::has_single_bit(BITS) && BITS >= CHAR_BIT && BITS <= Math::BitCntOf<uintmax_t>;

	template <size_t BITS>
	struct IntFor;

	template <size_t BITS> requires(!ccAlignedBits<BITS>)
	struct IntFor<BITS>
	{
		using type = UBitInt<BITS>;
	};

	template <size_t BITS> requires ccAlignedBits<BITS>
	struct IntFor<BITS>
	{
		using type = UIntFor::BitCnt<BITS>;
	};

	template <size_t BITS>
	using type = typename IntFor<BITS>::type;

	template <auto BEGIN, auto END>
	struct IntForRng
	{
		static constexpr auto _traits = [] -> std::pair<size_t, bool> {
			if constexpr (BEGIN >= 0)
				return {Math::bit_width(END), false};
			else if constexpr (END < 0)
				return {Math::bit_width(BEGIN), true};
			else
				return {std::max(Math::bit_width(BEGIN), Math::bit_width(END) + 1), true};
		}();
		using type = std::conditional_t<_traits.second, SIntFor::BitCnt<_traits.first>, UIntFor::BitCnt<_traits.first>>;
	};
} // namespace pw_pbl::_ranged_int

namespace pw_pbl::inline ranged_int
{
	enum class RIntError : uint8_t
	{
		OutOfRange, //< 值超出范围
		OutOfStep,	//< 值不符合步进
	};

	/// 限定值范围的整型值
	/// @tparam BITS 实际存储的位宽
	/// @tparam BEGIN 有效起始值
	/// @tparam END 有效结束值
	/// @tparam DEFAULT 默认值，当存储值为0时，对外表现的值，用于简化需要0值初始化却默认值不为0的情况
	/// @tparam STEP 步进值，默认为1，当STEP>1时，存储时可能会消除步进值
	/// @note EStepTrimed用来判断是否消除了步进值，即值存储前除去STEP
	/// @note ENarrowed用来判断是否缩减位宽，即存储原始值与起始值的差值而不是原始值，EStepTrimed为真时ENarrowed也必为真，且存储的是消除步进后的差值
	template <size_t BITS, auto BEGIN, auto END, auto DEFAULT = BEGIN, auto STEP = 1>
	struct RInt
	{
		static_assert(BEGIN <= DEFAULT && DEFAULT <= END && STEP >= 1 && BEGIN % STEP == 0 && END % STEP == 0 && DEFAULT % STEP == 0);

		using value_type = typename _ranged_int::IntForRng<BEGIN, END>::type; //< 表现值类型，可存储整个值区间
		static constexpr value_type EBegin = BEGIN;
		static constexpr value_type EEnd = END;
		static constexpr value_type EDefault = DEFAULT;
		static constexpr value_type EStep = STEP;
		static constexpr auto EIsSigned = std::is_signed_v<value_type>;
		static constexpr auto EBits = BITS;								  //< 存储位宽
		using store_type = _ranged_int::type<EBits>;					  //< 存储值类型，可能缩减位宽
		static_assert(EBits >= Math::bit_width((EEnd - EBegin) / EStep)); //< 位宽必须至少够存储减宽并消除步进后的值

		static constexpr auto ENarrowed = EBits < Math::BitCntOf<value_type>;														//< 存储位宽小于值位宽，是缩减宽度的
		static constexpr auto EStepTrimed = EBits < Math::bit_width(EEnd - EBegin);													//< 存储位宽甚至不足以存储差值，需要进一步消除冗余步进值
		static constexpr value_type EValueOffset = EStepTrimed ? EBegin / EStep : (ENarrowed ? EBegin : EDefault);					//< 减宽前基值，full_store = value - EValueOffset
		static constexpr store_type EStoreOffset = EStepTrimed ? (EDefault - EBegin) / EStep : (ENarrowed ? EDefault - EBegin : 0); //< 减宽偏移值，store = store_type(full_store - EStoreOffset)

		static_assert(EDefault == (EValueOffset + EStoreOffset) * (EStepTrimed ? EStep : 1)); // 通常 store = value - EEDefault，但减宽的情况下失去高位冗余，需要拆分为EBase和EOffset两步计算

		store_type store;

		static constexpr Result<void, RIntError> check(value_type v)
		{
			if (v < EBegin || EEnd < v)
				return Err(RIntError::OutOfRange);
			else if (EStep > 1 && v % EStep != 0)
				return Err(RIntError::OutOfStep);
			else
				return Ok();
		}
		constexpr Result<value_type, RIntError> value() const
		{
			if (value_type v = value_unchecked(); v < EBegin || EEnd < v)
				return Err(RIntError::OutOfRange);
			else if (!EStepTrimed && v % EStep != 0)
				return Err(RIntError::OutOfStep);
			else
				return Ok(v);
		}
		constexpr value_type value_or_default() const { return value().unwrap_ok_or(EDefault); }
		constexpr value_type value_unchecked() const
		{
			value_type out;
			if constexpr (EStoreOffset == 0 && EValueOffset == 0)
				out = store;
			else if constexpr (EStoreOffset == 0)
				out = store + EValueOffset;
			else
				out = store_type(store + EStoreOffset) + EValueOffset;
			if constexpr (EStepTrimed)
				return out * EStep;
			else
				return out;
		}
		static constexpr RInt gen_unchecked(value_type v)
		{
			pw_pbl_assert(check(v).is_ok(), "value out of range");
			if constexpr (EDefault != 0)
				v -= EDefault;
			if constexpr (EStepTrimed)
				v /= EStep;
			return {.store = static_cast<store_type>(v)};
		}
		static constexpr Result<RInt, RIntError> gen(value_type v)
		{
			return check(v).map_ok([v] { return gen_unchecked(v); });
		}
		constexpr value_type read() const { return value_or_default(); }
		constexpr bool write(value_type v)
		{
			if (check(v))
			{
				this->store = gen_unchecked(v).store;
				return true;
			}
			else
				return false;
		}
	};

	template <typename T>
	concept ccRangedInt = requires { RInt(std::declval<T>()); };

	struct NBool
	{
		bool raw;

		constexpr NBool() = default;
		constexpr NBool(bool v) : raw(!v) {}
		constexpr operator bool() const { return !raw; }

		static constexpr NBool new_with_raw(bool v) { return NBool(!v); }
	};
} // namespace pw_pbl::inline ranged_int

#endif // PW_PBL_RANGEDINT_HPP
